package com.hardydou.framework.auth;

import cn.hutool.core.util.StrUtil;
import cn.hutool.core.util.URLUtil;
import cn.hutool.log.Log;
import cn.hutool.log.LogFactory;
import com.hardydou.system.model.LoginUserDetails;
import com.hardydou.system.model.SystemRes;
import org.springframework.security.authentication.AnonymousAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.web.FilterInvocation;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.util.*;

/**
 * @author ：hardy
 * @description：基于url 权限验证 添加在用户名验证以后
 * @date ：Created in 2020-03-08
 * @Copyright ：Hardydou.com
 */
@Component
public class AppAuthService {

    private static Log LOG = LogFactory.get(AppAuthService.class);


    /**
     * 资源依赖关系
     */
    private static Map<String, Set> RES_DEPEND = new HashMap<>();


    public boolean auth(HttpServletRequest request, Authentication authentication) throws IOException, ServletException {
        boolean result;
        result = cacheAuth(request);
        if (!result) {
            result = simpleAuth(request, authentication);
        }
        if (result) {
            cache(request);
        }
        LOG.info("Auth - req : " + request.getMethod() + "\t" + request.getRequestURI() + "\t" + result);
        return result;
    }

    public boolean cacheAuth(HttpServletRequest request) {
        String uri = request.getRequestURI();
        String refer = request.getHeader("Referer"), referPath = "";
        Set<String> resSet = new HashSet<>();
        if (StrUtil.isNotEmpty(refer)) {
            referPath = URLUtil.getPath(refer);
            resSet = getDepSet(referPath);
        }
        Set<String> history = getCache(request);
        LOG.info("Auth - cache : " + history);
        if (history.contains(uri)) {
            return true;
        } else if (history.contains(referPath)) {
            if (resSet.contains(uri)) {
                return true;
            }
        }
        return false;
    }

    public void cache(HttpServletRequest request) {
        Set<String> tt = getCache(request);
        tt.add(request.getRequestURI());
    }

    public Set<String> getCache(HttpServletRequest request) {
        Object history = request.getSession().getAttribute("ResAuth");
        if (history == null) {
            history = new HashSet<String>();
        }
        request.getSession().setAttribute("ResAuth", history);
        return (Set<String>) history;

    }

    public static void addDepRes(String res, String depRes) {
        Set deps = getDepSet(res);
        deps.add(depRes);
    }

    public static void addDepRes(String res, Set<String> depResSet) {
        Set deps = getDepSet(res);
        deps.addAll(depResSet);
        LOG.info("Auth - deps : " + RES_DEPEND);
    }

    public static Set<String> getDepSet(String res) {
        Set deps = RES_DEPEND.get(res);
        if (deps == null) {
            deps = new HashSet();
        }
        RES_DEPEND.put(res, deps);
        return deps;
    }


    private boolean simpleAuth(HttpServletRequest request, Authentication authentication) {
        String requestPath = request.getRequestURI();
        String refer = request.getHeader("Referer"), referPath = "";
        Set<String> resSet = new HashSet<>();
        if (StrUtil.isNotEmpty(refer)) {
            referPath = URLUtil.getPath(refer);
            resSet = getDepSet(referPath);
        }
        LOG.info("Auth - refer :" + referPath
                + "\nAuth - dep : " + resSet);

        Object principal = authentication.getPrincipal();
        if (principal == null) {
            return false;
        }

        if (authentication instanceof AnonymousAuthenticationToken) {
            //check if this uri can be access by anonymous
            return false;
        }

        if ("/".equals(requestPath)) {
            return true;
        }
        //登录用户
        boolean hasPerm = false;
        LoginUserDetails loginUser = (LoginUserDetails) principal;
        Collection<? extends GrantedAuthority> authorities = loginUser.getAuthorities();
        for (GrantedAuthority authority : authorities) {
            if (!(authority instanceof SystemRes)) {
                continue;
            }
            SystemRes urlGrantedAuthority = (SystemRes) authority;
            if (StringUtils.isEmpty(urlGrantedAuthority.getResPath())) {
                continue;
            }
            //拥有当前请求资源权限
            else if (urlGrantedAuthority.getResPath().equals(requestPath)) {
                hasPerm = true;
                break;
            }
            //拥有refer资源权限，且当前资源是refer依赖资源
            else if ("/".equals(referPath) || urlGrantedAuthority.getResPath().equals(referPath)) {
                if (resSet.contains(requestPath)) {
                    hasPerm = true;
                    break;
                }
            }
        }
        return hasPerm;
    }

}
